package com.zelin.manager.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.zyg.common.utils.PageUtils;
import com.zyg.common.utils.Query;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;


import com.zelin.manager.dao.BrandDao;
import com.zelin.manager.entity.BrandEntity;
import com.zelin.manager.service.BrandService;


@Service("brandService")
public class BrandServiceImpl extends ServiceImpl<BrandDao, BrandEntity> implements BrandService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        //1. 构造查询包装器
        QueryWrapper<BrandEntity> wrapper = new QueryWrapper<BrandEntity>();
        //2. 得到参数key
        String key = (String) params.get("key");
        System.out.println("key = " + key);

        //3. 判断是否有值，添加各种查询条件
        if(StringUtils.isNotBlank(key)){
            wrapper.like("name", key );
            wrapper.or().like("first_char",key );
            wrapper.or().like("id",key);
        }
        //4. 开始条件带分页查询
        IPage<BrandEntity> page = this.page(
                new Query<BrandEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    /**
     * 功能: 分布式锁 [实现方式一：Lua + Redis实现]：不能自动续期
     * 参数:
     * 返回值: java.util.List<com.zelin.manager.entity.BrandEntity>
     * 时间: 2021/11/30 15:16
     */
    @Override
    public List<BrandEntity> findAll() {
        List<BrandEntity> brandEntities = null;
        //1. 生成一个随机的uuid字符串
        String uuid = UUID.randomUUID().toString();
        //2. 加锁
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 30, TimeUnit.SECONDS);
        if(lock){       //2.1 加锁成功
            try {
                //2. 得到redisTemplate中的品牌列表
                String brandStr = redisTemplate.opsForValue().get("brandListRedis");
                if(StrUtil.isNotBlank(brandStr)){    //2.1 得到所有的品牌列表
                    System.out.println("缓存命中...");
                    brandEntities = JSON.parseArray(brandStr, BrandEntity.class);
                }else{                               //2.2 得不到值就从数据库查询并放到redis中
                    System.out.println("---------[从数据库中取数据：]---------");
                    List<BrandEntity> list = this.list();
                    redisTemplate.opsForValue().set("brandListRedis",JSON.toJSONString(list));
                }
            } finally { //3. 使用lua脚本释放锁
                String scriptStr = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
                RedisScript<Long> script = new DefaultRedisScript(scriptStr,Long.class);
                redisTemplate.execute(script, Arrays.asList("lock"), uuid);
            }
        }else{          //2.2 加锁失败
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            findAll();  //自旋
        }
        return brandEntities;
    }
    /**
     * 功能: 分布式锁 [实现方式二：Redission实现]：
     * 参数:
     * 返回值: java.util.List<com.zelin.manager.entity.BrandEntity>
     * 时间: 2021/11/30 15:16
     */
    @Override
    public List<BrandEntity> findAll2() {
        List<BrandEntity> brandEntities = null;
        //1. 获取哪把锁
        RLock lock = redissonClient.getLock("lock");
        //2. 加锁
        lock.lock(20,TimeUnit.SECONDS);
        try {
            //2. 得到redisTemplate中的品牌列表
            String brandStr = redisTemplate.opsForValue().get("brandListRedis");
            if(StrUtil.isNotBlank(brandStr)){    //2.1 得到所有的品牌列表
                System.out.println("缓存命中(redission)...");
                brandEntities = JSON.parseArray(brandStr, BrandEntity.class);
            }else{                               //2.2 得不到值就从数据库查询并放到redis中
                System.out.println("---------[从数据库中取数据(redission)：]---------");
                List<BrandEntity> list = this.list();
                redisTemplate.opsForValue().set("brandListRedis",JSON.toJSONString(list));
            }
        } finally { //3. 使用redisson释放锁
           lock.unlock();
        }

        return brandEntities;
    }

}